using System;
using System.Linq;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using LS.UnityExtensions;
using UnityEngine;

namespace LS.Digitize
{
    [UnityEngine.Scripting.Preserve]
    public abstract class KeyValuePairCollectionSerializer : CompoundSerializer, IDictionarySerializer
    {
        public override bool IsUnsupported
        {
            get
            {
                return SubTypes.Count < 2;
            }
        }

        public override bool IsDictionary => true;
        public ISerializer KeySerializer => SubSerializer;
        public ISerializer ValueSerializer => SubSerializer2;

        public KeyValuePairCollectionSerializer(Type type) : base(type)
        {
            var types = LSReflection.GetElementTypes(type);
            subTypes = types;
        }
        
        public KeyValuePairCollectionSerializer(Type type, Type keyType, Type valueType) : base(type, keyType, valueType)
        {
            
        }

        public virtual bool ReadCollection(IReader reader, ICollection<KeyValuePair<object, object>> collection, ISerializer keySerializer, ISerializer valueSerializer, out bool nullCollection)
        {
            bool result = true;
            nullCollection = false;
            using (var scop = reader.ReadDictionaryScope())
            {
                if (!scop.ReadFailure)
                {
                    if (reader.CouldBeNull())
                    {
                        reader.ReadNull();
                        nullCollection = true;
                        return false;
                    }
                    foreach (KeyValuePair<object, object> pair in new ReadItemEnumerable(reader, KeySerializer, ValueSerializer))
                    {
                        collection.Add(pair);
                    }
                }
                else
                {
                    result = false;
                }
            }

            return result;
        }

        public override void ReadInto(IReader reader, ref object obj)
        {
            throw new NotImplementedException();
        }
        
        public struct ReadItemEnumerable : IEnumerable
        {
            private IReader m_Reader;
            private ISerializer m_KeySerializer;
            private ISerializer m_ValueSerializer;
            
            public ReadItemEnumerable(IReader reader, ISerializer keySerializer, ISerializer valueSerializer)
            {
                this.m_Reader = reader;
                this.m_KeySerializer = keySerializer;
                this.m_ValueSerializer = valueSerializer;
            }

            public IEnumerator GetEnumerator()
            {
                while (true)
                {
                    object key = null;
                    object value = null;
                    
                    using (var keyScope = m_Reader.ReadDictionaryKeyScope())
                    {
                        if(keyScope.ReadFailure) break;
                        key = m_KeySerializer.Read(m_Reader);
                    }

                    using (var valueScope = m_Reader.ReadDictionaryValueScope())
                    {
                        if (valueScope.ReadFailure)
                            value = null;
                        else
                        {
                            value = m_ValueSerializer.Read(m_Reader);
                        }
                    }
                    
                    yield return KeyValuePair.Create(key, value);
                }
            }
        }
    }

    [UnityEngine.Scripting.Preserve]
    public abstract class KeyValuePairCollectionSerializer<T, TItemKey, TItemValue>: CompoundSerializer<T>, IDictionarySerializer<T, TItemKey, TItemValue> where T : IDictionary<TItemKey, TItemValue>
    {
        public override bool IsUnsupported
        {
            get
            {
                return SubTypes.Count < 2;
            }
        }

        public override bool IsDictionary => true;
        public ISerializer KeySerializer => SubSerializer;
        public ISerializer ValueSerializer => SubSerializer2;

        public KeyValuePairCollectionSerializer(Type type) : base(type)
        {
            var types = LSReflection.GetElementTypes(type);
            subTypes = types;
        }
        
        public KeyValuePairCollectionSerializer(Type type, Type keyType, Type valueType) : base(type, keyType, valueType)
        {
            
        }

        public override void ReadInto(IReader reader, ref T obj)
        {
            throw new NotImplementedException();
        }
        
        public bool ReadCollection(IReader reader, ICollection<KeyValuePair<object, object>> collection, ISerializer keySerializer, ISerializer valueSerializer, out bool nullCollection)
        {
            bool result = true;
            nullCollection = false;
            using (var scop = reader.ReadDictionaryScope())
            {
                if (!scop.ReadFailure)
                {
                    if (reader.CouldBeNull())
                    {
                        reader.ReadNull();
                        nullCollection = true;
                        return false;
                    }
                    
                    foreach (KeyValuePair<object, object> pair in new ReadItemEnumerable<object, object>(reader, KeySerializer, ValueSerializer))
                    {
                        collection.Add(pair);
                    }
                }
                else
                {
                    result = false;
                }
            }

            return result;
        }
        
        public virtual bool ReadCollection(IReader reader, ICollection<KeyValuePair<TItemKey, TItemValue>> collection, ISerializer keySerializer, ISerializer valueSerializer, out bool nullCollection)
        {
            bool result = true;
            nullCollection = false;
            using (var scop = reader.ReadDictionaryScope())
            {
                if (!scop.ReadFailure)
                {
                    if (reader.CouldBeNull())
                    {
                        reader.ReadNull();
                        nullCollection = true;
                        return false;
                    }
                    
                    foreach (KeyValuePair<TItemKey, TItemValue> pair in new ReadItemEnumerable<TItemKey, TItemValue>(reader, KeySerializer, ValueSerializer))
                    {
                        collection.Add(pair);
                    }
                }
                else
                {
                    result = false;
                }
            }

            return result;
        }
        
        public virtual void ReadICollectionInto(IReader reader, ICollection collection, ISerializer elementSerializer)
        {
            throw new NotImplementedException();
        }

        public struct ReadItemEnumerable<TKey,TValue> : IEnumerable<KeyValuePair<TKey,TValue>>
        {
            private IReader m_Reader;
            private ISerializer m_KeySerializer;
            private ISerializer m_ValueSerializer;

            public ReadItemEnumerable(IReader reader, ISerializer keySerializer,ISerializer valueSerializer)
            {
                this.m_Reader = reader;
                this.m_KeySerializer = keySerializer;
                this.m_ValueSerializer = valueSerializer;
            }

            IEnumerator<KeyValuePair<TKey,TValue>> IEnumerable<KeyValuePair<TKey,TValue>>.GetEnumerator()
            {
                ISerializer<TKey> keySerializer = m_KeySerializer as ISerializer<TKey>;
                ISerializer<TValue> valueSerializer = m_ValueSerializer as ISerializer<TValue>;
                if (keySerializer != null && valueSerializer != null)
                {
                    while (true)
                    {
                        TKey key = default;
                        TValue value = default;
                    
                        using (var keyScope = m_Reader.ReadDictionaryKeyScope())
                        {
                            if(keyScope.ReadFailure) break;
                            key = keySerializer.ReadData(m_Reader);
                        }

                        using (var valueScope = m_Reader.ReadDictionaryValueScope())
                        {
                            if (!valueScope.ReadFailure)
                                value = valueSerializer.ReadData(m_Reader);
                        }
                    
                        yield return KeyValuePair.Create(key, value);
                    }
                }
                else
                {
                    while (true)
                    {
                        TKey key = default;
                        TValue value = default;
                    
                        using (var keyScope = m_Reader.ReadDictionaryKeyScope())
                        {
                            if(keyScope.ReadFailure) break;
                            key = (TKey)m_KeySerializer.Read(m_Reader);
                        }

                        using (var valueScope = m_Reader.ReadDictionaryValueScope())
                        {
                            if (!valueScope.ReadFailure)
                            {
                                object v = m_ValueSerializer.Read(m_Reader);
                                if(v != null)
                                    value = (TValue)v;
                            }
                        }
                    
                        yield return KeyValuePair.Create(key, value);
                    }
                }
            }

            public IEnumerator GetEnumerator()
            {
                while (true)
                {
                    object key = null;
                    object value = null;
                    
                    using (var keyScope = m_Reader.ReadDictionaryKeyScope())
                    {
                        if(keyScope.ReadFailure) break;
                        key = m_KeySerializer.Read(m_Reader);
                    }

                    using (var valueScope = m_Reader.ReadDictionaryValueScope())
                    {
                        if (valueScope.ReadFailure)
                            value = null;
                        else
                        {
                            value = m_ValueSerializer.Read(m_Reader);
                        }
                    }
                    
                    yield return KeyValuePair.Create(key, value);
                }
            }
        }

    }
}